package com.example.netty.nio;

import java.io.*;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.SocketAddress;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Arrays;

public class FileChanel {
    public static void main(String[] args) throws IOException {
//        fileInputOutputCopy();

        //ApI复制
//        APICopy();
//
////        ObjectIO();
//
////        readOnlyBuffer();
//
////        mappered();

        scatterAndGatherBuffer();
    }


    /**
     * 多buffer
     */
    public static void scatterAndGatherBuffer() throws IOException {
        //使用网络IO
        //创建channel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //创建端口
        InetSocketAddress inetSocketAddress = new InetSocketAddress(6666);

        //绑定
        serverSocketChannel.bind(inetSocketAddress);
        //启动
        serverSocketChannel.socket();

        //声明buffer数组
        ByteBuffer[] byteBuffers = new ByteBuffer[2];
        byteBuffers[0] = ByteBuffer.allocate(5);
        byteBuffers[1] = ByteBuffer.allocate(1);

        //等待客户连接
        SocketChannel socketChannel = serverSocketChannel.accept();

        int messageLength = 6;
        int byRead = 0;
        while (true) {
            byRead = 0;
            while (byRead<messageLength) {
                socketChannel.read(byteBuffers);
                byRead++;

                //输出buffer读取后内存情况
                Arrays.stream(byteBuffers).map(buffer->{
                    return "position:"+buffer.position()+" limite:"+buffer.limit();
                }).forEach(System.out::println);
            }

            //读写转换
            Arrays.asList(byteBuffers).forEach(buffer->buffer.flip());

            //读取
            int byWrite = 0;
            while (byWrite < messageLength) {
                long write = socketChannel.write(byteBuffers);
                byWrite+=write;
            }
            Arrays.asList(byteBuffers).forEach(buffer->buffer.clear());

            //输出读取的长度，输出的长度，最大的长度
            System.out.println("readNum："+byRead+" WriteNum:"+byWrite+" maxNum:"+messageLength);
        }

    }

    /**
     * 内存映射channel,不用将数据复制到cache
     */
    public static void mappered() throws FileNotFoundException {
        //randomAccessFile有多种模式同时可读可写
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(".\\file.txt","rw")) {
            FileChannel channel = randomAccessFile.getChannel();
            //映射的模式，开始位置，空间大小
            MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
            map.put(0, (byte) 'H');

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 只读buffer
     */
    public static void readOnlyBuffer() {
        ByteBuffer buffer = ByteBuffer.allocate(50);
        for (int i = 0; i < 10; i++) {
            buffer.put((byte) i);
        }

        ByteBuffer asReadOnlyBuffer = buffer.asReadOnlyBuffer();
        for (int i = 0; i < 10; i++) {
            System.out.println(asReadOnlyBuffer.get());
        }
        //不可写入,会报错
        asReadOnlyBuffer.put((byte) 12);
    }

    /**
     * 类型化输入输出
     */
    public static void ObjectIO() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        buffer.putInt(1);
        buffer.putLong(2L);
        buffer.putDouble(1.2);
        buffer.putChar('c');
        buffer.putFloat(1.2F);

        buffer.flip();
        //按顺序读取
        System.out.println(buffer.getInt());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getDouble());
        System.out.println(buffer.getChar());
        System.out.println(buffer.getFloat());
    }

    /**
     * 文件的输入输出和复制
     * @throws IOException
     */
    private static void fileInputOutputCopy() throws IOException {
        //写入文件
        //信息
        String message = "hello word";
        //输出流
        FileOutputStream fileOutputStream = new FileOutputStream(new File(".\\file.txt"));
        //获取chanel
        FileChannel channel = fileOutputStream.getChannel();
        //声明buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        //将信息写入buffer
        byteBuffer.put(message.getBytes("UTF-8"));
        //读写转换
        byteBuffer.flip();
        //读取buffer里的信息，写入chanel
        int read = channel.write(byteBuffer);


        //读取文件
        File file = new File(".\\file.txt");
        FileInputStream fileInputStream = new FileInputStream(file);
        FileChannel channel1 = fileInputStream.getChannel();
        ByteBuffer byteBuffer1 = ByteBuffer.allocate((int) file.length());
        //读取channel里的信息，写入buffer
        channel1.read(byteBuffer1);
        System.out.println(new String(byteBuffer1.array()));

        //文件复制
        byteBuffer.clear();
        File file1 = new File(".\\file2.txt");
        FileOutputStream fileOutputStream1 = new FileOutputStream(file1);
        FileChannel channel2 = fileOutputStream1.getChannel();
        while (true) {
            //读取channel里的信息，写入buffer
            int read1 = channel1.read(byteBuffer);
            byteBuffer.flip();
            //判断channel是否已经没有信息了
            if (read1 == -1) {
                break;
            }
            //读取buffer里的信息，写入channel
            channel2.write(byteBuffer);
            //再次翻转，以便下次的读取
            byteBuffer.flip();
        }

        //关闭流
        fileOutputStream.close();
        fileInputStream.close();
        fileOutputStream1.close();
    }

    /**
     * API文件复制
     * @throws IOException
     */
    public static void APICopy() throws IOException {
        FileInputStream fileInputStream = new FileInputStream(".\\dog.jpg");
        FileOutputStream fileOutputStream = new FileOutputStream(".\\dogCopy.jpg");

        FileChannel inputStreamChannel = fileInputStream.getChannel();
        FileChannel outputStreamChannel = fileOutputStream.getChannel();

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        //用输出通道调用复制API
        outputStreamChannel.transferFrom(inputStreamChannel,0,inputStreamChannel.size());

        //关闭通道
        inputStreamChannel.close();
        outputStreamChannel.close();
        fileInputStream.close();
        fileOutputStream.close();
    }
}
